## Container Types
type Seq [out T]
    native; // container.Seq
type List [out T]
    native; // container.List, ([] T)
type FlexList [out T]
    native; // container.FlexList
type Set [out T]
    native; // container.Set
type Map [out K, out V]
    native; // container.Map
type Queue [out T]
    native; // container.Queue
type PriorityQueue [out T]
    native; // container.PriorityQueue

## Auxiliary Types for Containers
type FlexListKey
    String;

## Container Operations

/* Functions for Seq[T] */

export function List:[T]
    &(Seq[T]) => List[T]
    native 'seq-collect';
export function shift:[T]
    &(Seq[T]) => Maybe[(T,Seq[T])]
    native 'seq-shift';
export function is-nil:[T]
    &(Seq[T]) => Bool
    &(seq) => { not seq.{shift}.{Bool} };

export const Nil:
    Seq[never]
    := native 'seq-nil';
export function cons:[T]
    &(T,Seq[T]) => Seq[T]
    native 'seq-cons';

export function Seq:
    & { from: Number, to: Number } => Seq[Number]
    native 'seq-range-inclusive';
export function Seq:
    & { initial: Number, count: Number } => Seq[Number]
    native 'seq-range-count';

export function map:[A,B]
    &(Seq[A], &(A) => B) => Seq[B]
    native 'seq-map';
export function filter-map:[A,B]
    &(Seq[A], &(A) => Maybe[B]) => Seq[B]
    native 'seq-filter-map';
export function filter:[T]
    &(Seq[T], &(T) => Bool) => Seq[T]
    native 'seq-filter';
export function flat-map:[A,B]
    &(Seq[A], &(A) => Seq[B]) => Seq[B]
    native 'seq-flat-map';

export function scan:[T,A]
    &(Seq[T], A, &(A,T) => A) => Seq[A]
    native 'seq-scan';
export function scan:[T,A]
    &(Seq[T], (A, &(A,T) => A)) => Seq[A]
    &(seq, opts) => let (init,f) := opts, { scan (seq,init,f) };
export function reduce:[T,A]
    &(Seq[T], A, &(A,T) => A) => A
    native 'seq-reduce';
export function reduce:[T,A]
    &(Seq[T], (A, &(A,T) => A)) => A
    &(seq, opts) => let (init,f) := opts, { reduce (seq,init,f) };
export function apply-all:[T]
    &(T, Seq[&(T) => T]) => T
    &(init, @f) => @f.{ reduce (init, &(x,f) => { f(x) }) };

export function some:[T]
    &(Seq[T], &(T) => Bool) => Bool
    native 'seq-some';
export function every:[T]
    &(Seq[T], &(T) => Bool) => Bool
    native 'seq-every';

export function chunk:[T]
    &(Seq[T], Number) => Seq[List[T]]
    native 'seq-chunk';
export function concat:[T]
    &(List[Seq[T]]) => Seq[T]
    &(array) => array.{Seq}.{flat-map(&(items) => items)};


/* Functions of List[T] */

export function Seq:[T]
    &(List[T]) => Seq[T]
    native 'list-iterate';
export function length:[T]
    &(List[T]) => Number
    native 'list-length';
export function reverse:[T]
    &(List[T]) => List[T]
    native 'list-reverse';

export function shift:[T]
    &(List[T]) => Maybe[(T,List[T])]
    native 'list-shift';
export function pop:[T]
    &(List[T]) => Maybe[(List[T],T)]
    native 'list-pop';
export function unshift:[T]
    &(List[T]) => List[T]
    native 'list-unshift';
export function unpop:[T]
    &(List[T]) => List[T]
    native 'list-unpop';

export function map:[A,B]
    &(List[A], &(A) => B) => List[B]
    &(list, f) => { Seq list } .{ map f } .{ List };
export function filter:[T]
    &(List[T], &(T) => Bool) => List[T]
    &(list, f) => { Seq list } .{ filter f } .{ List };
export function reduce:[T,A]
    &(List[T], (A, &(A,T) => A)) => A
    &(list, opts) => { Seq list } .{ reduce opts };
export function scan:[T,A]
    &(List[T], (A, &(A,T) => A)) => List[A]
    &(list, opts) => { Seq list } .{ scan opts } .{ List };
export function some:[T]
    &(List[T], &(T) => Bool) => Bool
    &(list, p) => ({ Seq list } some p);
export function every:[T]
    &(List[T], &(T) => Bool) => Bool
    &(list, p) => ({ Seq list } every p);

/* Functions of Set[T] */

export function new-set:[T]
    &(&(T,T) => Ordering, List[T]) => Set[T]
    native 'new-set';
export function has:[T]
    &(Set[T], T) => Bool
    native 'set-has';

/* Functions of Map[K,V] */

export function Map:[T]
    &(List[(String,T)]) => Map[String,T]
    native 'create-map-str';
export function List:[K,V]
    &(Map[K,V]) => List[(K,V)]
    native 'map-entries';
export function get:[K,V]
    &(Map[K,V], K) => Maybe[V]
    native 'map-get';
export function get!:[K,V]
    &(Map[K,V], K) => V
    native 'map-get!';
export function insert:[K,V]
    &(Map[K,V], (K, V)) => Maybe[Map[K,V]]
    native 'map-insert';
export function insert*:[K,V]
    &(Map[K,V], (K, V)) => Map[K,V]
    native 'map-insert*';
export function delete:[K,V]
    &(Map[K,V], K) => Maybe[(V,Map[K,V])]
    native 'map-delete';
export function delete*:[K,V]
    &(Map[K,V], K) => Map[K,V]
    native 'map-delete*';


/* Functions for FlexList[T] */

export function FlexList:[T]
    &(List[T], &(T) => FlexListKey) => FlexList[T]
    native 'create-flex';
export function FlexList:[T]
    &() => FlexList[T]
    native 'create-flex-empty';
export function Seq:[T]
    &(FlexList[T]) => Seq[(FlexListKey,T)]
    native 'flex-iterate';
export function length:[T]
    &(FlexList[T]) => Number
    native 'flex-length';
export function has:[T]
    &(FlexList[T], FlexListKey) => Bool
    native 'flex-has';
export function get:[T]
    &(FlexList[T], FlexListKey) => T
    native 'flex-get';
export function update:[T]
    &(FlexList[T], FlexListKey, &(T) => T) => FlexList[T]
    native 'flex-update';
export function delete:[T]
    &(FlexList[T], FlexListKey) => FlexList[T]
    native 'flex-delete';
export function prepend:[T]
    &(FlexList[T], (FlexListKey,T)) => FlexList[T]
    native 'flex-prepend';
export function append:[T]
    &(FlexList[T], (FlexListKey,T)) => FlexList[T]
    native 'flex-append';
export function insert:[T]
    &(FlexList[T], {before:FlexListKey}, (FlexListKey,T)) => FlexList[T]
    native 'flex-insert-before';
export function insert:[T]
    &(FlexList[T], {after:FlexListKey}, (FlexListKey,T)) => FlexList[T]
    native 'flex-insert-after';
export function move:[T]
    &(FlexList[T], FlexListKey, {before:FlexListKey}) => FlexList[T]
    native 'flex-move-before';
export function move:[T]
    &(FlexList[T], FlexListKey, {after:FlexListKey}) => FlexList[T]
    native 'flex-move-after';
export function move-up:[T]
    &(FlexList[T], FlexListKey) => FlexList[T]
    native 'flex-move-up';
export function move-down:[T]
    &(FlexList[T], FlexListKey) => FlexList[T]
    native 'flex-move-down';
export function swap:[T]
    &(FlexList[T], FlexListKey, FlexListKey) => FlexList[T]
    native 'flex-swap';

// TODO: type ListItemIndexWatch protected Observable[Number]
export function is-first-in:[T]
    &(Computed[Number], Reactive[FlexList[T]]) => Computed[Bool]
    &(@index, _) =>
        @index
            . { map &(index) => (index = 0) };
export function is-last-in:[T]
    &(Computed[Number], Reactive[FlexList[T]]) => Computed[Bool]
    &(@index,@list) =>
        let @length := ({ watch @list } map &(list) => list.{length}),
        { combine (@index, @length) }
            . { map &(index,length) => (index = (length - 1)) };

export function update:[T]
    &(Reactive[FlexList[T]], FlexListKey, &(T) => T) => Sync
    &(@list,key,f) => @list.{ update &(list) => {update(list,key,f)} };
export function delete:[T]
    &(Reactive[FlexList[T]], FlexListKey) => Sync
    &(@list,key) => @list.{ update &(list) => {delete(list,key)} };
export function prepend:[T]
    &(Reactive[FlexList[T]], (FlexListKey,T)) => Sync
    &(@list,entry) => @list.{ update &(list) => {prepend(list,entry)} };
export function append:[T]
    &(Reactive[FlexList[T]], (FlexListKey,T)) => Sync
    &(@list,entry) => @list.{ update &(list) => {append(list,entry)} };
export function insert:[T]
    &(Reactive[FlexList[T]], {before:FlexListKey}, (FlexListKey,T)) => Sync
    &(@list,pos,entry) => @list.{ update &(list) => {insert(list,pos,entry)} };
export function insert:[T]
    &(Reactive[FlexList[T]], {after:FlexListKey}, (FlexListKey,T)) => Sync
    &(@list,pos,entry) => @list.{ update &(list) => {insert(list,pos,entry)} };
export function move:[T]
    &(Reactive[FlexList[T]], FlexListKey, {before:FlexListKey}) => Sync
    &(@list,key,pos) => @list.{ update &(list) => {move(list,key,pos)} };
export function move:[T]
    &(Reactive[FlexList[T]], FlexListKey, {after:FlexListKey}) => Sync
    &(@list,key,pos) => @list.{ update &(list) => {move(list,key,pos)} };
export function move-up:[T]
    &(Reactive[FlexList[T]], FlexListKey) => Sync
    &(@list,key) => @list.{ update &(list) => {move-up(list,key)} };
export function move-down:[T]
    &(Reactive[FlexList[T]], FlexListKey) => Sync
    &(@list,key) => @list.{ update &(list) => {move-down(list,key)} };
export function swap:[T]
    &(Reactive[FlexList[T]], FlexListKey, FlexListKey) => Sync
    &(@list,key1,key2) => @list.{ update &(list) => {swap(list,key1,key2)} };
